feat(FM.2): warm observe-only ambient monitor daemon - #699
Conversation
doberman monitor run/status score non-inline activity through the same decision engine, alert-only, never enforcing. Closes DobermanCore#237
fu351
left a comment
There was a problem hiding this comment.
Thanks @Maqbool61! The observe-only boundary and no-learning tests are useful safeguards. Before merging, please keep failed alert writes retryable, retain collector instances across ticks, remove exception payloads from logs, make single-instance admission atomic, and distinguish ambient alerts in the dashboard’s badges and counts. I reproduced lost alerts after a failed insert and duplicate admission during simultaneous starts. Once this lands, I’d love you to take #238 (level-5), which adds the collectors directly on top of your daemon.
…omic admission, dashboard badges - record_decision returns bool; a failed write holds the cursor back instead of losing the alert - collectors discovered once, retained across ticks - exception logs carry only the error class, never message/traceback - single-instance admission via an atomic O_CREAT|O_EXCL lock file - doberman dash badges/counts distinguish ambient alerts from real BLOCK/AUTH
Thanks for the detailed review, @fu351! Addressed all five:
Repro'd your two bugs directly before fixing (lost alert after a forced |
|
Merged, thanks @Maqbool61! Having the daemon check whether the row actually landed before it advances the bus cursor was the decision that made this safe, an alert can be retried but never silently lost, and the import-boundary test keeps the monitor from ever reaching the engine. Nothing changed in your code, I only used GitHub's update-branch to bring it level with main before the rebase merge. This closes #237. One gap I left for a follow-up, #703: doberman tune still reads ambient rows into the friction report. The next piece is #238 (level-5), the FM.3 collectors that feed the daemon you just built, would love for you to take it. |
Closes #237.
doberman monitor run/doberman monitor status— the warm, observe-onlydaemon on top of the FM.1 activity bus. Each tick: polls every
doberman.collectorsentry point, emits onto the bus, drains from thedaemon's own saved cursor (crash-resumable, no replay/loss), and scores each
event through the same
decide()the live gate uses — recording an alert rowvia
record_decision(..., source_context_override="ambient:<collector_id>").Hard rules, each with its own test:
monitor/daemon.pynever statically importsdoberman.authordoberman.proxy.doberman.explain,doberman.render(used by
doberman log/status), anddoberman tuiall detect the"ambient:"source-context shape and prefix explanations with"observed (not enforced): ", replacing "blocked"/"pending auth" languagewith a neutral alert word. Structured output (
--jsonl) keeps the trueverdict for scripts/SIEMs — only human-facing text is softened.
revealed-preference table before and after a real multi-tick daemon run
and asserts byte-for-byte equality, plus an import-boundary test that the
daemon never reaches
doberman.subjective.baseline/drift/martingale/revealed.test (mirroring
test_import_boundaries.py's existing pattern) exercisesthe "Policy core must not depend on the ambient monitor"
import-lintercontract programmatically, not just via CI's
lint-importsstep.(
ReasonCode.ambient_scoring_error) are both covered directly.Also: a heartbeat + single-instance guard (refuses a second
monitor runforthe same repo),
doberman monitor statusfor liveness/cursor/backlog, anddocs/changelog updates.
CI: all 12 required checks pass (lint/boundaries/docs parity, package
smoke tests on ubuntu/windows, secret-scan, and the full test matrix across
3.11/3.12/3.13). Locally I saw two failures unrelated to this change — a
GUI-geometry test sensitive to local font metrics, and an integration test
whose
system_site_packagesvenv fixture can't see a project-venv install —neither reproduces here in CI, confirming they're local-environment-only.